/*
	K&R Unit tests
	NB: around a half of the tests are designed to fail or abort.
	I. e. some tests must fail. If they don't fail, it's a bug.
 */

#include <stdio.h>

#include "kru.h"

/*
	Tests that end up in signals
	These should always abort
 */
static void testPageFault(void *unused)
{
	char *ptr;

	ptr = NULL;
	*ptr = 'A';
}

static void testDivByZero(void *unused)
{
	double d, z, r;

	d = 10.0;
	z = 0.0;
	r = d / z;
}

/*
	Tests of regular assertions
 */
static void testPassS(void *unused)
{
	KRU_PASS("worked fine");
}

static void testFailF(void *unused)
{
	KRU_FAIL("shouldn't happen");
}

static void testFatalF(void *unused)
{
	KRU_FATAL("mustn't ever happen");
}

static void testAssertS(void *unused)
{
	int a, b;
	a = 42;
	b = 42;
	KRU_ASSERT(a == b);
	KRU_PASS("testAssertS passed");
}

static void testAssertF(void *unused)
{
	int a, b;
	a = 42;
	b = 41;
	KRU_ASSERT(a == b);
	KRU_PASS("testAssertF passed");
}

static void testAssertFatalS(void *unused)
{
	int a, b;
	a = 42;
	b = 42;
	KRU_ASSERT_FATAL(a == b);
	KRU_PASS("testAssertFatalS passed");
}

static void testAssertFatalF(void *unused)
{
	int a, b;
	a = 42;
	b = 41;
	KRU_ASSERT_FATAL(a == b);
	KRU_FAIL("42==41??? Wrong Universe detected!");
}

static void testAssertTrueS(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_TRUE(42 == a);
}

static void testAssertTrueF(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_TRUE(42 != a);
}

static void testAssertTrueFatalS(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_TRUE_FATAL(42 == a);
}

static void testAssertTrueFatalF(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_TRUE_FATAL(42 != a);
	KRU_FAIL("can't get here");
}

static void testAssertFalseS(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_FALSE(42 != a);
}

static void testAssertFalseF(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_FALSE(42 == a);
}

static void testAssertFalseFatalS(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_FALSE_FATAL(42 != a);
}

static void testAssertFalseFatalF(void *unused)
{
	int a;

	a = 42;
	KRU_ASSERT_FALSE_FATAL(42 == a);
	KRU_FAIL("can't get here");
}

static void testAssertEqualS(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 42;
	KRU_ASSERT_EQUAL(a, *c);
}

static void testAssertEqualF(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 41;
	KRU_ASSERT_EQUAL(a, *c);
}

static void testAssertEqualFatalS(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 42;
	KRU_ASSERT_EQUAL_FATAL(a, *c);
}

static void testAssertEqualFatalF(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 41;
	KRU_ASSERT_EQUAL_FATAL(a, *c);
	KRU_FAIL("can't get here");
}

static void testAssertNotEqualS(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 41;
	KRU_ASSERT_NOT_EQUAL(a, *c);
}

static void testAssertNotEqualF(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 42;
	KRU_ASSERT_NOT_EQUAL(a, *c);
}

static void testAssertNotEqualFatalS(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 41;
	KRU_ASSERT_NOT_EQUAL_FATAL(a, *c);
}

static void testAssertNotEqualFatalF(void *unused)
{
	int a, b;
	int *c;

	c = &b;
	a = 42;
	b = 42;
	KRU_ASSERT_NOT_EQUAL_FATAL(a, *c);
	KRU_FAIL("can't get here");
}

static void testAssertPtrEqualS(void *unused)
{
	int a;
	int *ptr1, *ptr2;

	a = 42;
	ptr1 = &a;
	ptr2 = &a;
	KRU_ASSERT_PTR_EQUAL(ptr1, ptr2);
}

static void testAssertPtrEqualF(void *unused)
{
	int a, b;
	int *ptr1, *ptr2;

	a = 42;
	b = 42;
	ptr1 = &a;
	ptr2 = &b;
	KRU_ASSERT_PTR_EQUAL(ptr1, ptr2);
}

static void testAssertPtrEqualFatalS(void *unused)
{
	int a;
	int *ptr1, *ptr2;

	a = 42;
	ptr1 = &a;
	ptr2 = &a;
	KRU_ASSERT_PTR_EQUAL_FATAL(ptr1, ptr2);
}

static void testAssertPtrEqualFatalF(void *unused)
{
	int a, b;
	int *ptr1, *ptr2;

	a = 42;
	b = 42;
	ptr1 = &a;
	ptr2 = &b;
	KRU_ASSERT_PTR_EQUAL_FATAL(ptr1, ptr2);
	KRU_FAIL("can't get here");
}

static void testAssertPtrNotEqualS(void *unused)
{
	int a, b;
	int *ptr1, *ptr2;

	a = 42;
	b = 42;
	ptr1 = &a;
	ptr2 = &b;
	KRU_ASSERT_PTR_NOT_EQUAL(ptr1, ptr2);
}

static void testAssertPtrNotEqualF(void *unused)
{
	int a;
	int *ptr1, *ptr2;

	a = 42;
	ptr1 = &a;
	ptr2 = &a;
	KRU_ASSERT_PTR_NOT_EQUAL(ptr1, ptr2);
}

static void testAssertPtrNotEqualFatalS(void *unused)
{
	int a, b;
	int *ptr1, *ptr2;

	a = 42;
	b = 42;
	ptr1 = &a;
	ptr2 = &b;
	KRU_ASSERT_PTR_NOT_EQUAL_FATAL(ptr1, ptr2);
}

static void testAssertPtrNotEqualFatalF(void *unused)
{
	int a;
	int *ptr1, *ptr2;

	a = 42;
	ptr1 = &a;
	ptr2 = &a;
	KRU_ASSERT_PTR_NOT_EQUAL_FATAL(ptr1, ptr2);
	KRU_FAIL("can't get here");
}

static void testAssertPtrNullS(void *unused)
{
	int *ptr;

	ptr = NULL;
	KRU_ASSERT_PTR_NULL(ptr);
}

static void testAssertPtrNullF(void *unused)
{
	int a;
	int *ptr;

	a = 42;
	ptr = &a;
	KRU_ASSERT_PTR_NULL(ptr);
}

static void testAssertPtrNullFatalS(void *unused)
{
	int *ptr;

	ptr = NULL;
	KRU_ASSERT_PTR_NULL_FATAL(ptr);
}

static void testAssertPtrNullFatalF(void *unused)
{
	int a;
	int *ptr;

	a = 42;
	ptr = &a;
	KRU_ASSERT_PTR_NULL_FATAL(ptr);
	KRU_FAIL("can't get here");
}

static void testAssertPtrNotNullS(void *unused)
{
	int a;
	int *ptr;

	a = 42;
	ptr = &a;
	KRU_ASSERT_PTR_NOT_NULL(ptr);
}

static void testAssertPtrNotNullF(void *unused)
{
	int *ptr;

	ptr = NULL;
	KRU_ASSERT_PTR_NOT_NULL(ptr);
}

static void testAssertPtrNotNullFatalS(void *unused)
{
	int a;
	int *ptr;

	a = 42;
	ptr = &a;
	KRU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
}

static void testAssertPtrNotNullFatalF(void *unused)
{
	int *ptr;

	ptr = NULL;
	KRU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
	KRU_FAIL("can't get here");
}

/* String to test */
static char *string1 = "String";
/* Textually equals to 'string1' */
static char *string2 = "String";
/* CI string to test */
static char *string3 = "STRING";
/* CI textually equals to 'string3' */
static char *string4 = "string";
/* Not equals to other strings */
static char *string5 = "Other string";

static void testAssertStrEqualS(void *unused)
{
	KRU_ASSERT_STR_EQUAL(string1, string2);
}

static void testAssertStrEqualF(void *unused)
{
	KRU_ASSERT_STR_EQUAL(string1, string5);
}

static void testAssertStrEqualFatalS(void *unused)
{
	KRU_ASSERT_STR_EQUAL_FATAL(string1, string2);
}

static void testAssertStrEqualFatalF(void *unused)
{
	KRU_ASSERT_STR_EQUAL_FATAL(string1, string5);
	KRU_FAIL("can't get here");
}

static void testAssertStrNotEqualS(void *unused)
{
	KRU_ASSERT_STR_NOT_EQUAL(string1, string5);
}

static void testAssertStrNotEqualF(void *unused)
{
	KRU_ASSERT_STR_NOT_EQUAL(string1, string2);
}

static void testAssertStrNotEqualFatalS(void *unused)
{
	KRU_ASSERT_STR_NOT_EQUAL_FATAL(string1, string5);
}

static void testAssertStrNotEqualFatalF(void *unused)
{
	KRU_ASSERT_STR_NOT_EQUAL_FATAL(string1, string2);
	KRU_FAIL("can't get here");
}

static void testAssertIStrEqualS(void *unused)
{
	KRU_ASSERT_ISTR_EQUAL(string3, string4);
}

static void testAssertIStrEqualF(void *unused)
{
	KRU_ASSERT_ISTR_EQUAL(string3, string5);
}

static void testAssertIStrEqualFatalS(void *unused)
{
	KRU_ASSERT_ISTR_EQUAL_FATAL(string3, string4);
}

static void testAssertIStrEqualFatalF(void *unused)
{
	KRU_ASSERT_ISTR_EQUAL_FATAL(string3, string5);
	KRU_FAIL("can't get here");
}

static void testAssertIStrNotEqualS(void *unused)
{
	KRU_ASSERT_ISTR_NOT_EQUAL(string3, string5);
}

static void testAssertIStrNotEqualF(void *unused)
{
	KRU_ASSERT_ISTR_NOT_EQUAL(string3, string4);
}

static void testAssertIStrNotEqualFatalS(void *unused)
{
	KRU_ASSERT_ISTR_NOT_EQUAL_FATAL(string3, string5);
}

static void testAssertIStrNotEqualFatalF(void *unused)
{
	KRU_ASSERT_ISTR_NOT_EQUAL_FATAL(string3, string4);
	KRU_FAIL("can't get here");
}

static const double f1 = 42.0001;
static const double f2 = 42.0;
static const double f3 = 42.003;
static const double dev = 0.0005;

static void testAssertDblEqualS(void *unused)
{
	KRU_ASSERT_DBL_EQUAL(f1, f2, dev);
}

static void testAssertDblEqualF(void *unused)
{
	KRU_ASSERT_DBL_EQUAL(f1, f3, dev);
}

static void testAssertDblEqualFatalS(void *unused)
{
	KRU_ASSERT_DBL_EQUAL_FATAL(f1, f2, dev);
}

static void testAssertDblEqualFatalF(void *unused)
{
	KRU_ASSERT_DBL_EQUAL_FATAL(f1, f3, dev);
	KRU_FAIL("can't get here");
}

static void testAssertDblNotEqualS(void *unused)
{
	KRU_ASSERT_DBL_NOT_EQUAL(f1, f3, dev);
}

static void testAssertDblNotEqualF(void *unused)
{
	KRU_ASSERT_DBL_NOT_EQUAL(f1, f2, dev);
}

static void testAssertDblNotEqualFatalS(void *unused)
{
	KRU_ASSERT_DBL_NOT_EQUAL_FATAL(f1, f3, dev);
}

static void testAssertDblNotEqualFatalF(void *unused)
{
	KRU_ASSERT_DBL_NOT_EQUAL_FATAL(f1, f2, dev);
	KRU_FAIL("can't get here");
}

extern void heap_valid_report();

int main(void)
{
	kru_registry *registry;
	kru_suite *suite;

	heap_valid_report();

	registry = kru_registry_new("K&R Unit test");

	suite = kru_registry_add_new_suite(registry, "HW errors", NULL, NULL);
	kru_suite_add_new_test(suite, "testPageFault", NULL, testPageFault, NULL);
	kru_suite_add_new_test(suite, "testDivByZero", NULL, testDivByZero, NULL);

	suite = kru_registry_add_new_suite(registry, "SW errors", NULL, NULL);
	kru_suite_add_new_test(suite, "testPassS", NULL, testPassS, NULL);
	kru_suite_add_new_test(suite, "testFailF", NULL, testFailF, NULL);
	kru_suite_add_new_test(suite, "testFatalF", NULL, testFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertS", NULL, testAssertS, NULL);
	kru_suite_add_new_test(suite, "testAssertF", NULL, testAssertF, NULL);
	kru_suite_add_new_test(suite, "testAssertFatalS", NULL, testAssertFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertFatalF", NULL, testAssertFatalF, NULL);

	kru_suite_add_new_test(suite, "testAssertTrueS", NULL, testAssertTrueS, NULL);
	kru_suite_add_new_test(suite, "testAssertTrueF", NULL, testAssertTrueF, NULL);
	kru_suite_add_new_test(suite, "testAssertTrueFatalS", NULL, testAssertTrueFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertTrueFatalF", NULL, testAssertTrueFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertFalseS", NULL, testAssertFalseS, NULL);
	kru_suite_add_new_test(suite, "testAssertFalseF", NULL, testAssertFalseF, NULL);
	kru_suite_add_new_test(suite, "testAssertFalseFatalS", NULL, testAssertFalseFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertFalseFatalF", NULL, testAssertFalseFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertEqualS", NULL, testAssertEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertEqualF", NULL, testAssertEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertEqualFatalS", NULL, testAssertEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertEqualFatalF", NULL, testAssertEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertNotEqualS", NULL, testAssertNotEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertNotEqualF", NULL, testAssertNotEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertNotEqualFatalS", NULL, testAssertNotEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertNotEqualFatalF", NULL, testAssertNotEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrEqualS", NULL, testAssertPtrEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrEqualF", NULL, testAssertPtrEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrEqualFatalS", NULL, testAssertPtrEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrEqualFatalF", NULL, testAssertPtrEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotEqualS", NULL, testAssertPtrNotEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotEqualF", NULL, testAssertPtrNotEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotEqualFatalS", NULL, testAssertPtrNotEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotEqualFatalF", NULL, testAssertPtrNotEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNullS", NULL, testAssertPtrNullS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNullF", NULL, testAssertPtrNullF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNullFatalS", NULL, testAssertPtrNullFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNullFatalF", NULL, testAssertPtrNullFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotNullS", NULL, testAssertPtrNotNullS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotNullF", NULL, testAssertPtrNotNullF, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotNullFatalS", NULL, testAssertPtrNotNullFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertPtrNotNullFatalF", NULL, testAssertPtrNotNullFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertStrEqualS", NULL, testAssertStrEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertStrEqualF", NULL, testAssertStrEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertStrEqualFatalS", NULL, testAssertStrEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertStrEqualFatalF", NULL, testAssertStrEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertStrNotEqualS", NULL, testAssertStrNotEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertStrNotEqualF", NULL, testAssertStrNotEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertStrNotEqualFatalS", NULL, testAssertStrNotEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertStrNotEqualFatalF", NULL, testAssertStrNotEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrEqualS", NULL, testAssertIStrEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrEqualF", NULL, testAssertIStrEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrEqualFatalS", NULL, testAssertIStrEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrEqualFatalF", NULL, testAssertIStrEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrNotEqualS", NULL, testAssertIStrNotEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrNotEqualF", NULL, testAssertIStrNotEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrNotEqualFatalS", NULL, testAssertIStrNotEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertIStrNotEqualFatalF", NULL, testAssertIStrNotEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertDblEqualS", NULL, testAssertDblEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertDblEqualF", NULL, testAssertDblEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertDblEqualFatalS", NULL, testAssertDblEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertDblEqualFatalF", NULL, testAssertDblEqualFatalF, NULL);
	kru_suite_add_new_test(suite, "testAssertDblNotEqualS", NULL, testAssertDblNotEqualS, NULL);
	kru_suite_add_new_test(suite, "testAssertDblNotEqualF", NULL, testAssertDblNotEqualF, NULL);
	kru_suite_add_new_test(suite, "testAssertDblNotEqualFatalS", NULL, testAssertDblNotEqualFatalS, NULL);
	kru_suite_add_new_test(suite, "testAssertDblNotEqualFatalF", NULL, testAssertDblNotEqualFatalF, NULL);

	kru_registry_run(registry);
	kru_registry_print_report(registry);

	kru_registry_delete(registry);

	heap_valid_report();

	return 0;
}
